package com.clementheliou.cheliou.dal.dao.base.impl;

import org.hibernate.Session;
import org.hibernate.SessionFactory;
import org.hibernate.dialect.Dialect;
import org.hibernate.engine.spi.SessionFactoryImplementor;
import org.hibernate.stat.Statistics;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Required;
import org.springframework.orm.hibernate3.HibernateTemplate;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import com.clementheliou.cheliou.dal.dao.base.BaseDAO;

/**
 * Hibernate-based implementation of {@link BaseDAO} interface. A factory bean is autowired by
 * Spring, based on the module configuration file. Implemented methods will be performed on the
 * current session.
 * <p>
 * Due to the {@link Transactional} annotation and its values, we check there is a transaction but
 * we don't rollback at this point. Indeed, rollback will be performed in the business layer by
 * services.
 * </p>
 * 
 * @author Clément HELIOU (clement.heliou@gmail.com)
 * @see {@link BaseDAO}.
 * @see {@link Transactional}.
 * @since 1.0
 */
@Transactional(propagation = Propagation.MANDATORY, noRollbackFor = { Throwable.class })
public class BaseHibernateDAO implements BaseDAO, InitializingBean {

	/**
	 * Hibernate template linked to the session factory {@link #sessionFactory}.
	 * 
	 * @author Clément HELIOU (clement.heliou@gmail.com)
	 * @see {@link HibernateTemplate}.
	 * @since 1.0
	 */
	private HibernateTemplate hibernateTemplate;

	/**
	 * Hibernate local session factory.
	 * 
	 * @author Clément HELIOU (clement.heliou@gmail.com)
	 * @see {@link SessionFactory}.
	 * @since 1.0
	 */
	private SessionFactory sessionFactory;

	/**
	 * <i>{@inheritDoc}</i>
	 * 
	 * @author Clément HELIOU (clement.heliou@gmail.com)
	 * @since 1.0
	 */
	@Override
	public void clearSession() {
		getCurrentSession().clear();
	}

	/**
	 * <i>{@inheritDoc}</i>
	 * 
	 * @author Clément HELIOU (clement.heliou@gmail.com)
	 * @since 1.0
	 */
	@Override
	public Dialect getDialect() {
		return ((SessionFactoryImplementor) sessionFactory).getDialect();
	}

	/**
	 * {@inheritDoc}
	 * 
	 * @author Clément HELIOU (clement.heliou@gmail.com)
	 * @since 1.0
	 */
	@Override
	public void flushSession() {
		getCurrentSession().flush();
	}

	/**
	 * Enables statistics logs for the session factory.
	 * 
	 * @author Clément HELIOU (clement.heliou@gmail.com)
	 * @see {@link SessionFactory#getStatistics()};
	 * @see {@link Statistics}.
	 * @since 1.0
	 */
	public void enableStatistics() {
		this.sessionFactory.getStatistics().setStatisticsEnabled(true);
	}

	/**
	 * Clears the statistics logs of the session factory, flushes and cleans this last one.
	 * 
	 * @author Clément HELIOU (clement.heliou@gmail.com)
	 * @see {@link SessionFactory#getStatistics()};
	 * @see {@link Statistics}.
	 * @since 1.0
	 */
	public void clearStatistics() {
		this.sessionFactory.getStatistics().clear();
		flushSession();
		clearSession();
	}

	/**
	 * Gets the session factory from the application context and initializes the Hibernate template
	 * with it.
	 * 
	 * @author Clément HELIOU (clement.heliou@gmail.com)
	 * @param sessionFactory the session factory to use.
	 * @see {@link Autowired};
	 * @see {@link Required}.
	 * @since 1.0
	 */
	@Autowired
	@Required
	public void setSessionFactory(SessionFactory sessionFactory) {
		this.sessionFactory = sessionFactory;
		this.hibernateTemplate = new HibernateTemplate(this.sessionFactory);
	}

	/**
	 * <i>{@inheritDoc}</i>
	 * <p>
	 * Checks that a session factor has been successfully set.
	 * </p>
	 * 
	 * @author Clément HELIOU (clement.heliou@gmail.com)
	 * @since 1.0
	 */
	@Override
	public void afterPropertiesSet() throws Exception {
		Assert.notNull(sessionFactory, "A session factory must be provided at this time.");
	}

	/*
	 * Getters & setters
	 */

	protected HibernateTemplate getHibernateTemplate() {
		return hibernateTemplate;
	}

	protected Session getCurrentSession() {
		return sessionFactory.getCurrentSession();
	}

	public Statistics getSessionFactoryStatistics() {
		return sessionFactory.getStatistics();
	}
}
